from typing import List

from bson import ObjectId
from loguru import logger
from pymongo import ReturnDocument

from ioservice.common.basic import mdb
from ioservice.common.mongo import aggregate_docs
from ioservice.common.mongo import insert_models, insert_one_model, fake_delete_models_by_ids, find_many_models, \
    count_documents, bulk_update_models_through_id, check_models_exists_by_ids, aggregate_models
from ioservice.const import activity_col, activity_counter, institution_col, institution_counter, business_col
from ioservice.objectcode import bulk_generate_object_code, generate_object_code
from models.activity import Activity
from models.institution import Institution
from utils.dateutil import date_now


async def insert_one_activity(activity: Activity):
    activity.create_time = date_now()
    activity.unstore_file_url()
    insert_id = await insert_one_model(activity_col, activity)
    return insert_id


async def bulk_insert_activities(activities: List[Activity]):
    ocodes = await bulk_generate_object_code(activity_counter, len(activities))
    for ocode, a in zip(ocodes, activities):
        a.set_ocode(ocode)
        a.create_time = date_now()
        a.update_time = date_now()
        a.unstore_file_url()
        a.children = 0
        a.people_num = 0
        # fix unset
        # see bugs  https://github.com/samuelcolvin/pydantic/issues/1399
        if not a.co_inst_ids:
            a.co_inst_ids = []
        logger.error(f"co_inst_id:: {a.co_inst_ids}")
        await create_institution(a)

        if a.business_id or a.business_name:
            a.business_id = await upsert_business(a)

    inserted_ids = await insert_models(activity_col, activities)
    return inserted_ids


async def upsert_business(a: Activity):
    col = mdb.get_collection(business_col)
    query = {"name": a.business_name}
    if a.business_id:
        query["_id"] = ObjectId(a.business_id)

    db_business = await col.find_one_and_update(query, {"$set": query}, projection={"_id": True},
                                          upsert=True, return_document=ReturnDocument.AFTER)

    return db_business['_id']


async def create_institution(a: Activity):
    """case: 当用户手动创建新的外部机构的"""

    async def update_or_insert(institution) -> ObjectId:
        col = mdb.get_collection(institution_col)
        institution.set_ocode(await generate_object_code(institution_counter))
        institution.update_time = date_now()
        db_inst = await col.find_one_and_update({"institution_name": institution.institution_name,
                                                 "in_kexie": False,
                                                 "is_deleted": False}, {"$set": institution.dict(exclude_none=True)},
                                                projection={"_id": True},
                                                upsert=True, return_document=ReturnDocument.AFTER)
        return db_inst['_id']

    _update_or_insert = update_or_insert

    if a.institution_label:
        ins = Institution(institution_name=a.institution_label.strip(), in_kexie=False)
        insert_ins_id = await _update_or_insert(ins)
        a.institution_id = insert_ins_id

    if a.co_inst_label:
        ins = Institution(institution_name=a.co_inst_label.strip(), in_kexie=False)
        insert_ins_id = await _update_or_insert(ins)
        # create new activity
        a.co_inst_ids.append(insert_ins_id)


async def bulk_update_activities(activities: List[Activity]):
    for a in activities:
        a.create_time = date_now()
        a.update_time = date_now()
        a.unstore_file_url()
        if not a.co_inst_ids:
            a.co_inst_ids = []
        logger.error(f"co_inst_id:: {a.co_inst_ids}")
        await create_institution(a)

        if a.business_id or a.business_name:
            a.business_id = await upsert_business(a)

    return await bulk_update_models_through_id(activity_col, activities)


async def delete_many_activities_by_ids(activity_ids: List[str]):
    deleted_num = await fake_delete_models_by_ids(activity_col, activity_ids)
    return deleted_num


async def find_activities_by_query(query, sort=None, offset=0, size=20, fields=None):
    activities = await find_many_models(activity_col, Activity, query, sort, offset, size, fields)
    for a in activities:
        a.fill_file_url()
    return activities


async def aggregate_activities_raw(pipeline):
    return await aggregate_docs(activity_col, pipeline)


async def aggregate_activities(pipeline):
    activities = await aggregate_models(activity_col, pipeline, Activity)
    for a in activities:
        a.fill_file_url()
    return activities


async def count_activities(query):
    return await count_documents(activity_col, query)


async def check_activities_exist_by_ids(ids):
    return await check_models_exists_by_ids(activity_col, ids)

# async def visibility_activities_by_user():
#     await find_one_model(activity_col, )
